import { throttle } from '../utils'

const delay = 100 // 持续事件触发间隔

export function moveable(
  targetEl: HTMLElement,
  dragEl: HTMLElement,
  overflow: boolean = true,
  callback?: (e?: MouseEvent) => void
): () => void {
  let transform = {
    offsetX: 0,
    offsetY: 0
  }

  const onMousedown = (e) => {
    const style = window.getComputedStyle(targetEl)
    const left = Number.parseFloat(style.getPropertyValue('left'))
    const top = Number.parseFloat(style.getPropertyValue('top'))

    if (style.position != 'absolute') return

    transform = {
      offsetX: left,
      offsetY: top
    }

    const downX = e.clientX
    const downY = e.clientY
    const { offsetX, offsetY } = transform

    const targetRect = targetEl.getBoundingClientRect()
    const targetLeft = targetRect.left
    const targetTop = targetRect.top
    const targetWidth = targetRect.width
    const targetHeight = targetRect.height

    const clientWidth = document.documentElement.clientWidth
    const clientHeight = document.documentElement.clientHeight

    const minLeft = -targetLeft + offsetX
    const minTop = -targetTop + offsetY
    const maxLeft = clientWidth - targetLeft - targetWidth + offsetX
    const maxTop = clientHeight - targetTop - targetHeight + offsetY

    const onMousemove = throttle((e) => {
      let moveX = offsetX + e.clientX - downX
      let moveY = offsetY + e.clientY - downY

      if (!overflow) {
        moveX = Math.min(Math.max(moveX, minLeft), maxLeft)
        moveY = Math.min(Math.max(moveY, minTop), maxTop)
      }

      transform = {
        offsetX: moveX,
        offsetY: moveY
      }

      if (targetEl) {
        targetEl.style.left = `${moveX}px`
        targetEl.style.top = `${moveY}px`
      }

      callback?.(e)
    }, delay)

    const onMouseup = () => {
      document.removeEventListener('mousemove', onMousemove)
      document.removeEventListener('mouseup', onMouseup)
    }

    document.addEventListener('mousemove', onMousemove)
    document.addEventListener('mouseup', onMouseup)
  }

  dragEl.addEventListener('mousedown', onMousedown)

  return () => {
    dragEl.removeEventListener('mousedown', onMousedown)
  }
}

export function draggable(targetEl: HTMLElement, dragEl: HTMLElement) {
  const onMousedown = (e) => {
    if (window.getComputedStyle(targetEl).position == 'absolute') return

    targetEl.setAttribute('draggable', 'true')

    const onMouseup = (e) => {
      targetEl.setAttribute('draggable', 'false')
      document.removeEventListener('mouseup', onMouseup)
    }

    document.addEventListener('mouseup', onMouseup)
  }

  dragEl.addEventListener('mousedown', onMousedown)

  return () => {
    dragEl.removeEventListener('mousedown', onMousedown)
  }
}

export function resizeable(
  targetEl: HTMLElement,
  dragEl: HTMLElement,
  type: 0 | 1 | 2, // 0：横，1：竖，2：斜
  callback?: (e?: MouseEvent) => void
): () => void {
  const onMousedown = (e) => {
    const style = window.getComputedStyle(targetEl)
    const width = Number.parseFloat(style.getPropertyValue('width'))
    const height = Number.parseFloat(style.getPropertyValue('height'))

    const downX = e.clientX
    const downY = e.clientY

    const onMousemove = throttle((e) => {
      let moveX = e.clientX - downX + width
      let moveY = e.clientY - downY + height

      if (targetEl) {
        if (type === 0) targetEl.style.width = `${moveX}px`
        else if (type === 1) targetEl.style.height = `${moveY}px`
        else {
          targetEl.style.width = `${moveX}px`
          targetEl.style.height = `${moveY}px`
        }
      }

      callback?.(e)
    }, delay)

    const onMouseup = () => {
      document.removeEventListener('mousemove', onMousemove)
      document.removeEventListener('mouseup', onMouseup)
    }

    document.addEventListener('mousemove', onMousemove)
    document.addEventListener('mouseup', onMouseup)
  }

  dragEl.addEventListener('mousedown', onMousedown)

  return () => {
    dragEl.removeEventListener('mousedown', onMousedown)
  }
}

export function scaleable(targetEl: HTMLElement, callback?: (e?: MouseEvent) => void) {
  const onWheel = throttle((e) => {
    e.preventDefault()
    e.stopPropagation()

    const { deltaY } = e
    const speed = 0.0005

    const style = window.getComputedStyle(targetEl)
    const transform = style.getPropertyValue('transform')
    const match = transform.match(
      /matrix\(([-\d\.]+), ([-\d\.]+), ([-\d\.]+), ([-\d\.]+), ([-\d\.]+), ([-\d\.]+)\)/
    )

    let scaleX = +match?.[1] || 1
    const skewY = +match?.[2] || 0
    const skewX = +match?.[3] || 0
    let scaleY = +match?.[4] || 1
    const translateX = +match?.[5] || 0
    const translateY = +match?.[6] || 0

    scaleX += -deltaY * speed
    scaleY += -deltaY * speed

    targetEl.style.transform = `matrix(${scaleX}, ${skewY}, ${skewX}, ${scaleY}, ${translateX}, ${translateY})`

    callback?.(e)
  }, delay)

  targetEl.addEventListener('wheel', onWheel)

  return () => {
    targetEl.removeEventListener('wheel', onWheel)
  }
}
